import paho.mqtt.client as mqtt
import time
import json
import threading
import traceback
from typing import Callable, Optional, Any

class MQTTClient:
    def __init__(self, client_id: str, broker_host: str, broker_port: int = 1883, 
                 keepalive: int = 60, username: Optional[str] = None, 
                 password: Optional[str] = None):
        """初始化支持多线程的MQTT客户端"""
        self.client_id = client_id
        self.broker_host = broker_host
        self.broker_port = broker_port
        self.keepalive = keepalive
        self.vin = "master-00001"
        
        # 创建MQTT客户端实例
        self.client = mqtt.Client(client_id=client_id)
        
        # 设置用户名和密码（如果提供）
        if username and password:
            self.client.username_pw_set(username, password)
        
        # 绑定回调函数
        self.client.on_connect = self._on_connect
        self.client.on_message = self._on_message
        self.client.on_disconnect = self._on_disconnect
        self.client.on_publish = self._on_publish
        self.client.on_subscribe = self._on_subscribe
        
        # 存储用户定义的消息处理函数
        self.message_callback: Optional[Callable[[str, str], None]] = None
        
        # 连接状态 - 使用线程安全的Event对象
        self.connected_event = threading.Event()
        
        # 线程相关
        self.loop_thread: Optional[threading.Thread] = None
        self.running = False
        self.lock = threading.Lock()  # 用于线程安全操作

    def _on_connect(self, client, userdata, flags, rc):
        """连接回调函数"""
        if rc == 0:
            self.connected_event.set()  # 标记为已连接
            print(f"成功连接到MQTT Broker: {self.broker_host}:{self.broker_port}")
        else:
            self.connected_event.clear()  # 标记为未连接
            print(f"连接失败，错误代码: {rc}")
            # 错误代码解释
            error_messages = {
                1: "连接被拒绝 - 不正确的协议版本",
                2: "连接被拒绝 - 无效的客户端标识符",
                3: "连接被拒绝 - 服务器不可用",
                4: "连接被拒绝 - 不正确的用户名或密码",
                5: "连接被拒绝 - 未授权"
            }
            if rc in error_messages:
                print(f"错误原因: {error_messages[rc]}")

    def _on_message(self, client, userdata, msg):
        """消息接收回调函数"""
        payload = msg.payload.decode('utf-8')
        print(f"收到消息 - 主题: {msg.topic}, 内容: {payload}")
        
        # 如果用户定义了消息处理函数，调用它
        if self.message_callback:
            try:
                # 在新线程中处理消息，避免阻塞MQTT循环
                threading.Thread(
                    target=self.message_callback,
                    args=(msg.topic, payload),
                    daemon=True
                ).start()
            except Exception as e:
                print(f"处理消息时出错: {e}")

    def _on_disconnect(self, client, userdata, rc):
        """断开连接回调函数"""
        self.connected_event.clear()  # 标记为未连接
        if rc != 0:
            print(f"意外断开连接，错误代码: {rc}")
        else:
            print("已断开与MQTT Broker的连接")

    def _on_publish(self, client, userdata, mid):
        """消息发布回调函数"""
        print(f"消息发布成功，消息ID: {mid}")

    def _on_subscribe(self, client, userdata, mid, granted_qos):
        """订阅回调函数"""
        print(f"订阅成功，消息ID: {mid}, QoS: {granted_qos}")

    def _loop_forever(self):
        """在单独线程中运行的循环函数"""
        while self.running:
            try:
                # 运行网络循环，超时时间1秒
                self.client.loop(timeout=1.0)
            except Exception as e:
                print(f"MQTT循环出错: {e}")
                traceback.print_exc()
                time.sleep(1)
        
        # 循环结束后断开连接
        self.client.disconnect()
        print("MQTT循环线程已停止")

    def connect(self) -> bool:
        """连接到MQTT Broker并在新线程中启动循环"""
        # 只在获取锁后检查运行状态，减少锁占用时间
        with self.lock:
            if self.running:
                print("MQTT客户端已经在运行中")
                return self.connected_event.is_set()
        
        try:
            # 连接到 broker - 不在锁内执行，避免长时间占用
            print(f"尝试连接到 {self.broker_host}:{self.broker_port}")
            self.client.connect(self.broker_host, self.broker_port, self.keepalive)
            
            # 获取锁更新运行状态
            with self.lock:
                self.running = True
                
                # 创建并启动循环线程
                self.loop_thread = threading.Thread(
                    target=self._loop_forever,
                    daemon=True
                )
                self.loop_thread.start()
            
            # 等待连接成功，最多等待10秒
            print("等待连接确认...")
            connected = self.connected_event.wait(timeout=10.0)
            
            return connected
        except Exception as e:
            print(f"连接过程中发生错误: {e}")
            traceback.print_exc()
            with self.lock:
                self.running = False
            return False

    def disconnect(self):
        """断开与MQTT Broker的连接并停止线程"""
        with self.lock:
            if not self.running:
                return
            self.running = False
        
        # 等待线程结束
        if self.loop_thread:
            print("等待MQTT线程结束...")
            self.loop_thread.join(timeout=5.0)
            self.loop_thread = None
        self.connected_event.clear()

    def publish(self, topic: str, payload: Any, qos: int = 0, retain: bool = False) -> bool:
        """发布消息到指定主题（线程安全）"""
        # 先检查状态，减少锁占用时间
        if not self.connected_event.is_set():
            print("未连接到MQTT Broker，无法发布消息")
            return False
            
        try:
            # 处理payload - 不在锁内执行
            if isinstance(payload, dict):
                payload = json.dumps(payload)
            elif not isinstance(payload, str):
                payload = str(payload)
            
            # 只在必要操作时获取锁
            with self.lock:
                if not self.running:
                    print("MQTT客户端已停止，无法发布消息")
                    return False
                result = self.client.publish(topic, payload, qos, retain)
            
            # 等待消息发布完成 - 不在锁内执行
            result.wait_for_publish()
            return result.is_published()
        except Exception as e:
            print(f"发布消息时发生错误: {e}")
            return False

    def subscribe(self, topic: str, qos: int = 0) -> bool:
        """订阅指定主题（线程安全）"""
        # 先检查状态，减少锁占用时间
        if not self.connected_event.is_set():
            print("未连接到MQTT Broker，无法订阅主题")
            return False

        try:
            with self.lock:
                if not self.running:
                    print("MQTT客户端已停止，无法订阅主题")
                    return False
                result, mid = self.client.subscribe(topic, qos)
            
            return result == mqtt.MQTT_ERR_SUCCESS
        except Exception as e:
            print(f"订阅主题时发生错误: {e}")
            return False
        
    def validate_message(self, payload):
        """验证消息格式"""
        required_fields = ["VIN", "terminal_id", "frame_id", "sys_data_type", "time_stamp", "code", "data"]
        for field in required_fields:
            if field not in payload:
                print(f"消息缺少必要字段: {field}")
                return False
        return True

    def set_message_callback(self, callback: Callable[[str, str], None]):
        """设置自定义消息处理函数"""
        with self.lock:
            self.message_callback = callback

    def send_tesk(self,pont_id):
        response = {
            "VIN": self.vin,
            "terminal_id": 2,
            "frame_id": 933,
            "sys_data_type": 1004,
            "time_stamp": int(time.time()),
            "code": 10,
            "data": {
                "tasktype": 1,
                "coordinatePoint":[
                {
                    "x": 1.0,
                    "y": 1.0,
                    "theta": 1.0,
                    "point_id":401
                }
                ],
                "task_id": 231290841240
            }
        }